using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

namespace WorkFlow.Net.Process
{
    [RequireComponent(typeof(AccountInfo))]
    public class NetPlayerProcess : MonoBehaviour
    {
        private static int _gameState;

        private static string _treasureChestId;
        
        private static bool _isInit;
        
        private static string _startGameTime;
        private static string _endGameTime;

        public static int GameStatus => _gameState;
        
        public static bool Initialized => _isInit;
        
        [SerializeField] private UnityEvent onInitialized = new UnityEvent();
        [SerializeField] private UnityEvent<string,System.Action> onNetError = new UnityEvent<string,System.Action>();
        
        public UnityEvent OnInitializedEvent => onInitialized;
        public  UnityEvent<string,System.Action> OnNetError => onNetError;
        public static UnityEvent OnInitializedEventStatic => _instance.onInitialized;

        public static event System.Action OnInitialized;
        public static event System.Action OnInitializedActionOnce;
        
        private static List<string> _data;
        private static string _id;

        private static string _progressItemId;
        public static string ProgressItemId => _progressItemId;
        
        private static string _interventionRecordId;
        public static string InterventionRecordId => _interventionRecordId;

        public static void ReadCustomId(Dictionary<string, string> dictionary)
        {
            if (dictionary != null)
            {
                dictionary.TryGetValue("progressItemId",out _progressItemId);
                dictionary.TryGetValue("interventionRecordId",out _interventionRecordId);
            }
        }
        
        private IInitValueSetter InitValueSetter { get; set; }
        private IContinueOrRestart ContinueOrRestart { get; set; }
        public static string Value => _data is { Count: 0 } ? string.Empty : _data[^1];

        public static int Count => _data?.Count ?? 0;
        public static string HistoryValue(int index)
        {
            if (index < 0 || index >= _data.Count)
                return string.Empty;
            return _data[index];
        }

        /// <summary>
        /// 设置本地Value List的成员值，只更新在本地，会随着SyncListItem或SetProcess更新至服务器
        /// </summary>
        /// <param name="value"></param>
        /// <param name="index"></param>
        public static void SetListItem(string value, int index)
        {
            _data[index] = value;
        }
        
        public static void SyncListItem(System.Action<bool> callBack)
        {
            SetDataReq dataReq = new SetDataReq
            {
                Id = _id,
                Value = GetGameValue(),
                StartTime = string.IsNullOrEmpty(_startGameTime)?string.Empty:_startGameTime,
                EndTime = string.IsNullOrEmpty(_endGameTime)?string.Empty:_endGameTime,
                TreasureChestId = _treasureChestId,
                GameState = _gameState.ToString(),
                GameTag = NetPlayerPresData.Instance.GameTag
            };
            EnumeratorRunner.Instance.Run(PutURL(NetPlayerPresData.Instance.UpdateUrl, dataReq, NetPlayerPresData.Instance.Token, callBack));
        }
        
        [SerializeField]
        [Header("初次初始化时（服务器传GameValue为空）时或者外部调用ResetProcess时使用此值，但是InitValueSetter存在时还需走InitValueSetter的逻辑")] 
        private string initNetValue;

        static NetPlayerProcess _instance;
        private void Awake()
        {
            _instance = this;
            AccountInfo accountInfo = GetComponent<AccountInfo>();
            accountInfo.OnLoginSet.AddListener(OnLoginInfoUpdate); 
        }
        
        public void OnLoginInfoUpdate()
        {
            _treasureChestId = AccountInfo.Instance.TreasureChestId;
            if (!_isInit)
            {
                _isInit = true;
                InitValueSetter = GetComponent<IInitValueSetter>();
                ContinueOrRestart = GetComponent<IContinueOrRestart>();
                GetProcess(response =>
                {
                    StartCoroutine(StartProcess(response));
                });
            }
        }


        IEnumerator StartProcess(GetResponseData response)
        {
            var tempData = _data;
            _data = new ();
            bool isWrite = false;
            Next result = Next.Continue;
            _gameState = 0;
            if (response != null && ContinueOrRestart != null && response.GameState != "1")
            {
                ContinueOrRestart.Check(response.GameValue,response.GameState,response.StartTime,res =>
                {
                    isWrite = true;
                    result = res;
                });
                yield return new WaitUntil(() => isWrite);
                if (result == Next.Restart)
                {
                    
                    _data.AddRange(GetGameValueList(response.GameValue));
                    InnerResetProcess();
                    yield break;
                }
            }
            if (response == null || response.GameState == "1")
            {
                if (response != null)
                {
                    if(_data!=null)
                        _data.Clear();
                    else
                    {
                        _data = new List<string>();
                    }
                    _data.AddRange(GetGameValueList(response.GameValue));
                }
                InnerResetProcess();
            }
            else
            {
                _data.AddRange(GetGameValueList(response.GameValue));
                CheckId(response);
            }
        }

        private void CheckId(GetResponseData getResponseData)
        {
            if (getResponseData != null)
            {
                _startGameTime = getResponseData.StartTime;
                _id = getResponseData.Id;
            }
            else
            {
                Debug.LogError("GetResponseData is null");
            }

            var temp = OnInitializedActionOnce;
            OnInitializedActionOnce= null;
            Debug.Log("Net Player Process Initialized");
            temp?.Invoke();
            OnInitialized?.Invoke();
            onInitialized?.Invoke();
        }

        private void OnDestroy()
        {
            _isInit = false;
            _instance = null;
        }

        private static void SetGameStateValue(int gameState)
        {
            _gameState = gameState;
        }
        
        private static void InnerResetProcess()
        {
            var tempData = _data;
            _data = new ();
            _startGameTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            _endGameTime = string.Empty;
            string tempInitValue = _instance.initNetValue;
            if(_instance.InitValueSetter != null)
                tempInitValue = _instance.InitValueSetter.InitValue(tempData);
            //这里插入一个值
            Insert(tempInitValue, res =>
            {
                if (res)
                {
                    GetProcess(_instance.CheckId);
                }
            });
        }
        
        public static void ResetProcess(int gameState = -1)
        {
            if (gameState != -1)
                _gameState = gameState;
            var tempData = _data;
            _data = new ();
            _startGameTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            _endGameTime = string.Empty;
            string tempInitValue = _instance.initNetValue;
            if(_instance.InitValueSetter != null)
                tempInitValue = _instance.InitValueSetter.InitValue(tempData);
            //这里插入一个值
            Insert(tempInitValue, res =>
            {
                if (res)
                {
                    GetProcess(_instance.CheckId);
                }
            });
        }

        public static void SetProcess(string value,bool isEnd, System.Action<bool> callBack)
        {
            if (isEnd)
            {
                SetGameStateValue(1);
                _endGameTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            }
            SetDataReq dataReq = new SetDataReq
            {
                Id = _id,
                Value = GetGameValue(value),
                StartTime = string.IsNullOrEmpty(_startGameTime)?string.Empty:_startGameTime,
                EndTime = string.IsNullOrEmpty(_endGameTime)?string.Empty:_endGameTime,
                TreasureChestId = _treasureChestId,
                GameState = _gameState.ToString(),
                GameTag = NetPlayerPresData.Instance.GameTag
            };
            EnumeratorRunner.Instance.Run(PutURL(NetPlayerPresData.Instance.UpdateUrl, dataReq, NetPlayerPresData.Instance.Token, callBack));
        }

        private static void Insert(string value, System.Action<bool> callBack)
        {
            SetDataReq dataReq = new SetDataReq
            {
                Value = GetGameValue(value),
                StartTime = string.IsNullOrEmpty(_startGameTime)?string.Empty:_startGameTime,
                EndTime = string.IsNullOrEmpty(_endGameTime)?string.Empty:_endGameTime,
                TreasureChestId = _treasureChestId,
                GameState = _gameState.ToString(),
                GameTag = NetPlayerPresData.Instance.GameTag
            };
            EnumeratorRunner.Instance.Run(PostURL(NetPlayerPresData.Instance.InsertUrl, dataReq, NetPlayerPresData.Instance.Token, callBack));
        }
        
        public static void SetProcessAndReportAddress(string value, string reportAddress, System.Action<bool> callBack)
        {
            SetGameStateValue(1);
            _endGameTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            SetDataReq dataReq = new SetDataReq
            {
                Id = _id,
                Value = GetGameValue(value),
                TreasureChestId = _treasureChestId,
                StartTime = string.IsNullOrEmpty(_startGameTime)?string.Empty:_startGameTime,
                EndTime = string.IsNullOrEmpty(_endGameTime)?string.Empty:_endGameTime,
                ReportAddress = reportAddress,
                GameState = _gameState.ToString(),
                GameTag = NetPlayerPresData.Instance.GameTag
            };
            EnumeratorRunner.Instance.Run(PutURL(NetPlayerPresData.Instance.UpdateUrl, dataReq, NetPlayerPresData.Instance.Token, callBack));
        }

        public static void GetProcess(System.Action<GetResponseData> callBack)
        {
            EnumeratorRunner.Instance.Run(GetUrl(NetPlayerPresData.Instance.CheckUrl,
                _treasureChestId, NetPlayerPresData.Instance.Token,callBack));
        }

        public static void DelKey(string key, System.Action<GetResponseData> callBack)
        {
            EnumeratorRunner.Instance.Run(DelUrl(NetPlayerPresData.Instance.CheckUrl,
                _treasureChestId, NetPlayerPresData.Instance.Token,callBack));
        }

        private static IEnumerator PostURL(string url, SetDataReq dataReq, string token, System.Action<bool> callBack)
        {
            UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST);
            try
            {
                request.downloadHandler = new DownloadHandlerBuffer();

                request.SetRequestHeader("Authorization",$"Bearer {token}");
                request.SetRequestHeader("xySource", "CLIENT");
                
                string json = JsonConvert.SerializeObject(dataReq);
                
                Debug.Log($"Post Data: {json}");
                
                byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(json);

                request.uploadHandler = new UploadHandlerRaw(bodyRaw);
                request.SetRequestHeader("Content-Type", "application/json");

                yield return request.SendWebRequest();
                if (request.result == UnityWebRequest.Result.Success)
                {
                    Debug.Log("Response: " + request.downloadHandler.text);
                    var netResponse = JsonConvert.DeserializeObject<PostNetResponse>(request.downloadHandler.text);
                    if (netResponse.Success)
                    {
                        callBack?.Invoke(true);
                    }
                    else
                    {
                        _instance.onNetError?.Invoke("网络错误[NPP-PO0]", () =>
                        {
                            EnumeratorRunner.Instance.Run(PostURL(url, dataReq, token, callBack));
                        });
                        callBack?.Invoke(false);
                    }
                }
                else
                {
                    Debug.LogError("Error: " + request.error);
                    _instance.onNetError?.Invoke("网络错误[NPP-PO]", () =>
                    {
                        EnumeratorRunner.Instance.Run(PostURL(url, dataReq, token, callBack));
                    });
                }
            }
            finally
            {
                request.Dispose();
            }
        }

        private static IEnumerator PutURL(string url, SetDataReq dataReq, string token, System.Action<bool> callBack)
        {
            UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPUT);
            try
            {
                request.downloadHandler = new DownloadHandlerBuffer();

                request.SetRequestHeader("Authorization", $"Bearer {token}");
                request.SetRequestHeader("xySource", "CLIENT");
                
                string json = JsonConvert.SerializeObject(dataReq);
                
                Debug.Log($"Put Data: {json}");
                
                byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(json);

                request.uploadHandler = new UploadHandlerRaw(bodyRaw);
                request.SetRequestHeader("Content-Type", "application/json");

                yield return request.SendWebRequest();
                if (request.result == UnityWebRequest.Result.Success)
                {
                    Debug.Log("Response: " + request.downloadHandler.text);
                    var netResponse = JsonConvert.DeserializeObject<PostNetResponse>(request.downloadHandler.text);
                    if (netResponse.Success)
                    {
                        callBack?.Invoke(true);
                    }
                    else
                    {
                        _instance.onNetError?.Invoke("网络错误[NPP-PO0]", () =>
                        {
                            EnumeratorRunner.Instance.Run(PutURL(url, dataReq, token, callBack));
                        });
                        callBack?.Invoke(false);
                    }
                }
                else
                {
                    Debug.LogError("Error: " + request.error);
                    _instance.onNetError?.Invoke("网络错误[NPP-PU]", () =>
                    {
                        EnumeratorRunner.Instance.Run(PutURL(url, dataReq, token, callBack));
                    });
                }
            }
            finally
            {
                request.Dispose();
            }
        }
        
        private static IEnumerator GetUrl(string url, string treasureChestId, string token,System.Action<GetResponseData> callBack)
        {
            string urlStr = $"{url}?treasureChestId={treasureChestId}";
            Debug.Log("Get URL : " + urlStr + "Token " + token);
            UnityWebRequest request = new UnityWebRequest(urlStr, UnityWebRequest.kHttpVerbGET);
            try
            {
                request.downloadHandler = new DownloadHandlerBuffer();
                request.SetRequestHeader("Authorization", $"Bearer {token}");
                request.SetRequestHeader("xySource", "CLIENT");
                yield return request.SendWebRequest();
                if (request.result == UnityWebRequest.Result.Success)
                {
                    Debug.Log("Response: " + request.downloadHandler.text);
                    GetNetResponse netResponse = JsonConvert.DeserializeObject<GetNetResponse>(request.downloadHandler.text);
                    callBack?.Invoke(netResponse.Data);
                }
                else
                {
                    Debug.LogError("Error: " + request.error);
                    _instance.onNetError?.Invoke("网络错误[NPP-G]", () =>
                    {
                        EnumeratorRunner.Instance.Run(GetUrl(url, treasureChestId, token, callBack));
                    });
                }
            }
            finally
            {
                request.Dispose();
            }
        }

        private static IEnumerator DelUrl(string url, string treasureChestId, string token,System.Action<GetResponseData> callBack)
        {
            string urlStr = $"{url}?treasureChestId={treasureChestId}";
            Debug.Log("Del URL : " + urlStr);
            UnityWebRequest request = new UnityWebRequest(urlStr, UnityWebRequest.kHttpVerbGET);
            try
            {
                request.downloadHandler = new DownloadHandlerBuffer();
                request.SetRequestHeader("Authorization", token);
                request.SetRequestHeader("xySource", "CLIENT");
                yield return request.SendWebRequest();
                if (request.result == UnityWebRequest.Result.Success)
                {
                    Debug.Log("Response: " + request.downloadHandler.text);
                    GetNetResponse netResponse = JsonConvert.DeserializeObject<GetNetResponse>(request.downloadHandler.text);
                    callBack?.Invoke(netResponse.Data);
                }
                else
                {
                    Debug.LogError("Error: " + request.error);
                    _instance.onNetError?.Invoke("网络错误[NPP-D]", () =>
                    {
                        EnumeratorRunner.Instance.Run(DelUrl(url, treasureChestId, token, callBack));
                    });
                }
            }
            finally
            {
                request.Dispose();
            }
        }

        static string GetGameValue(string value)
        {
            if (_data.Count == 0 || !_data[^1].Equals(value, StringComparison.Ordinal))
                _data.AddRange(GetGameValueList(value));
            return $"[{string.Join(",", _data)}]";
        }

        static string GetGameValue()
        {
            return $"[{string.Join(",", _data)}]";
        }

        static string[] GetGameValueList(string value)
        {
            if (string.IsNullOrEmpty(value))
                return new string[]{};
            return value.TrimStart('[').TrimEnd(']').Split(',');
        }

        public class SetDataReq
        {
            [JsonProperty("id")] public string Id { get; set; }
            [JsonProperty("actionType")] public string ActionType { get; private set; } = "set";
            [JsonProperty("gameValue")] public string Value { get; set; }
            [JsonProperty("status")] public string GameState { get; set; }
            [JsonProperty("tag")] [JsonConverter(typeof(StringEnumConverter))] public GameTagEnum GameTag { get; set; }
            [JsonProperty("gameStartTime")] public string StartTime { get; set; }
            [JsonProperty("gameEndTime")] public string EndTime { get; set; }
            [JsonProperty("reportAddress")] public string ReportAddress { get; set; }
            [JsonProperty("treasureChestId")] public string TreasureChestId { get; set; }

            [JsonProperty("progressItemId")] public string ItemId { get; set; } = ProgressItemId;

            [JsonProperty("interventionRecordId")] public string RecordId { get; set; } = InterventionRecordId;
        }
        
        public class GetNetResponse
        {
            [JsonProperty("msg")] public string Msg { get; set; }
            [JsonProperty("code")] public int Code { get; set; }
            [JsonProperty("data")] public GetResponseData Data { get; set; }
            public bool Success => Code == 200;
        }
        
        public class PostNetResponse
        {
            [JsonProperty("msg")] public string Msg { get; set; }
            [JsonProperty("code")] public int Code { get; set; }
            [JsonProperty("data")] public bool Result { get; set; }
            public bool Success => Code == 200 || Result;
        }

        public class GetResponseData
        {
            [JsonProperty("id")] public string Id { get; set; }
            [JsonProperty("gameValue")] public string GameValue { get; set; }
            [JsonProperty("status")] public string GameState { get; set; }
            [JsonProperty("gameStartTime")] public string StartTime { get; set; }
        }

        public interface IInitValueSetter
        {
            string InitValue(List<string> data);
        }
        public enum Next
        {
            Continue,
            Restart
        }
        public interface IContinueOrRestart
        {
            /// <summary>
            /// 检查当前数据，决定是继续还是重新开始
            /// </summary> <param name="gameValue">存档的游戏数据</param>
            /// <param name="gameState">存档的游戏状态</param>
 			/// <param name="timeString">存档的时间</param>
            /// <param name="restart">回调，传入决定结果</param>
            void Check(string gameValue,string gameState,string timeString,System.Action<Next> restart);
        }
    }
}